using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Sirenix.OdinInspector;


public partial class DataListCreatorV2 : MonoBehaviour
{
    public RectTransform viewPrototype;
    public RectTransform wrapperPrototype;
    public RectTransform sepratorPrototype;
    public RectTransform content;
    public GameObjectPoolV2 pool;
  
    public event Action<RectTransform, object> ViewCreated;

    [ShowInInspector, ReadOnly]
    List<DataEntry> entryList = new List<DataEntry>();

    public class DataEntry
    {
        public object data;
        public RectTransform viewWrapper;
        public RectTransform view;
        public RectTransform headSeprator;
    }

    public Func<object, RectTransform> requestOverridePrototypeByData;

    public RectTransform GetFinalPrototype(object data)
    {
        if(requestOverridePrototypeByData == null)
        {
            return this.viewPrototype;
        }
        else
        {
            var prototype = requestOverridePrototypeByData?.Invoke(data);
            if(prototype == null)
            {
                throw new Exception("Not found prototype for data " + data.ToString());
            }
            return prototype;
        }
    }

    void Awake()
    {
        if (viewPrototype != null)
        {
            // 如果非预制体，就要先隐藏
            var scene = viewPrototype.gameObject.scene;
            if (!string.IsNullOrEmpty(scene.name))
            {
                viewPrototype.gameObject.SetActive(false);
            }
        }

        if (sepratorPrototype != null)
        {
            // 如果非预制体，就要先隐藏
            var scene = sepratorPrototype.gameObject.scene;
            if (!string.IsNullOrEmpty(scene.name))
            {
                sepratorPrototype.gameObject.SetActive(false);
            }
        }
    }

    public IEnumerable<DataEntry> GetAllEntry()
    {
        return this.entryList;
    }

    public DataEntry GetEntry(int index)
    {
        var entry = this.entryList[index];
        return entry;
    }

    public void ForeachViewWrapper(Action<RectTransform> handler)
    {
        foreach(var entry in this.entryList)
        {
            handler.Invoke(entry.viewWrapper);
        }
    }

    public void ForeachData(Action<object> handler)
    {
        foreach (var entry in this.entryList)
        {
            handler.Invoke(entry.data);
        }
    }

    public void ForeachEntry(Action<int, DataEntry> handler)
    {
        for(int i = 0; i <  this.entryList.Count; i++) 
        {
            var entry = this.entryList[i];
            handler.Invoke(i, entry);
        }
    }

    public RectTransform GetView(int index) => this.entryList[index].view;

    public RectTransform GetWrapper(int index) => this.entryList[index].viewWrapper;

    public DataEntry Insert(int index, object data)
    {
        var entry = new DataEntry();
        entry.data = data;

        if(this.sepratorPrototype != null)
        {
            // pzy:
            // 尚未对有分隔符的情况下提供支持
            throw new Exception("[DataListCreatorV2] insert while sepratorPrototype not null is not implement");
        }
        //if (this.sepratorPrototype != null)
        //{
        //    var headSeprator = this.pool.Reuse(this.sepratorPrototype, this.content.transform, true);
        //    entry.headSeprator = headSeprator;
        //}

        RectTransform view = null;
        if(this.wrapperPrototype != null)
        {
            var wrapper = this.pool.Reuse(this.wrapperPrototype, this.content.transform, true);
            wrapper.transform.SetSiblingIndex(index);
            var prototype = this.GetFinalPrototype(data);
            view = this.pool.Reuse(prototype, wrapper, true);
            //Debug.Log($"prototype.sizeDelta: {prototype.sizeDelta}, prototype.localScale: {prototype.localScale}，prototype.sizeDelta * prototype.localScale: {prototype.sizeDelta * prototype.localScale}");
            wrapper.sizeDelta = prototype.sizeDelta * prototype.localScale;
            entry.viewWrapper = wrapper;
        }
        else
        {
            var prototype = this.GetFinalPrototype(data);
            view = this.pool.Reuse(prototype, this.content.transform, true);
            view.transform.SetSiblingIndex(index);
        }
        entry.view = view;
        ViewCreated?.Invoke(view.transform as RectTransform, data);
        this.entryList.Insert(index, entry);
        
        if(entry.view != null)
        {
            entry.view.name = $"view - {index}";
        } 
        if(entry.viewWrapper != null)
        {
            entry.viewWrapper.name = $"wrapper - {index}";
        }
        return entry;
    }

    public void Reset(IEnumerable dataList)
    {
        this.CleanEntry();
        bool first = true;
        if(dataList != null)
        {
            foreach (var data in dataList)
            {
                this.Add(data, !first);
                first = false;
            }
        }
    }


    void Add(object data, bool hasSerator = true)
    {
        var entry = new DataEntry();
        entry.data = data;
        //var wrapper = this.CreateWrapper(this.content.transform);

        if(hasSerator)
        {
            if (this.sepratorPrototype != null)
            {
                var headSeprator = this.pool.Reuse(this.sepratorPrototype, this.content.transform, true);
                entry.headSeprator = headSeprator;
            }
        }

        RectTransform view;
        if(this.wrapperPrototype != null)
        {
            var wrapper = this.pool.Reuse(this.wrapperPrototype, this.content.transform, true);
            var prototype = this.GetFinalPrototype(data);
            view = this.pool.Reuse(prototype, wrapper, true);
            //wrapper.sizeDelta = prototype.sizeDelta;
            wrapper.sizeDelta = prototype.sizeDelta * prototype.localScale;
            entry.viewWrapper = wrapper;
        }
        else
        {
            var prototype = this.GetFinalPrototype(data);
            view = this.pool.Reuse(prototype, this.content.transform, true);
        }
        entry.view = view;
        ViewCreated?.Invoke(view.transform as RectTransform, data);

        var index = this.entryList.Count;

        this.entryList.Add(entry);

        if (entry.view != null)
        {
            entry.view.name = $"view - {index}";
        }
        if (entry.viewWrapper != null)
        {
            entry.viewWrapper.name = $"wrapper - {index}";
        }
    }

    public void RemoveAt(int index)
    {
        var entry = this.entryList[index];
        var view = entry.view;
        var viewWrapper = entry.viewWrapper;
        var headSeprator = entry.headSeprator;
        if(view != null)
        {
            this.pool.Recycle(entry.view.gameObject);
        }
        if(viewWrapper != null)
        {
            this.pool.Recycle(entry.viewWrapper.gameObject);
        }
        if(headSeprator != null)
        {
            this.pool.Recycle(entry.headSeprator.gameObject);
        }
        this.entryList.RemoveAt(index);
    }

    public int Count => this.entryList.Count;

    public void CleanEntry()
    {
        foreach(var entry in this.entryList)
        {
            var view = entry.view;
            var viewWrapper = entry.viewWrapper;
            var headSeprator = entry.headSeprator;
            if (view != null)
            {
                this.pool.Recycle(entry.view.gameObject);
            }
            if (viewWrapper != null)
            {
                this.pool.Recycle(entry.viewWrapper.gameObject);
            }
            if (headSeprator != null)
            {
                this.pool.Recycle(entry.headSeprator.gameObject);
            }
        }
        this.entryList.Clear();
    }

    public List<RectTransform> ViewList
    {
        get
        {
            var ret = new List<RectTransform>();
            foreach(var entry in this.entryList)
            {
                var view = entry.view;
                if(view != null)
                {
                    ret.Add(view);
                }
            }
            return ret;
        }
    }

    public List<DataEntry> DataEntryList
    {
        get
        {
            var ret = new List<DataEntry>();
            foreach (var entry in this.entryList)
            {
                ret.Add(entry);
            }
            return ret;
        }
    }

    public int GetIndexByView(RectTransform view)
    {
        for (int i = 0; i < this.entryList.Count; i++)
        {
            var entry = this.entryList[i];
            if(entry.view == view)
            {
                return i;
            }
        }
        return -1;
    }

    public int GetIndexByData(object data)
    {
        for (int i = 0; i < this.entryList.Count; i++)
        {
            var entry = this.entryList[i];
            if (entry.data == data)
            {
                return i;
            }
        }
        return -1;
    }

    public object GetData(int index) => this.entryList[index].data;
}